package com.csx.governance.traffic.adapter.outbound.resttemplate;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.csx.governance.traffic.adapter.config.TrafficLoadBalancerConfiguration;
import com.csx.governance.traffic.adapter.outbound.LoadBalancerInstanceSupplierBeanPostProcessor;
import com.csx.governance.traffic.adapter.outbound.TrafficServiceInstanceSupplier;
import com.csx.governance.traffic.adapter.outbound.openfeign.CustomFeignRequestInterceptor;
import com.csx.governance.traffic.core.context.TrafficRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p>负载均衡器的拦截器(自定义实现) - Spring Cloud LoadBalancer实现</p>
 * <p>主要作用是向由LoadBalancer创建的子容器中注册自定义的 {@link TrafficServiceInstanceSupplier} (内置自定义的服务实例过滤逻辑)</p>
 * <p>注: 该拦截器继承自 {@link ClientHttpRequestInterceptor} ,因此仅对于 {@link org.springframework.web.client.RestTemplate} 发起的外调生效;
 *     针对 {@link feign.Feign} 发起的外调, 可以参考 {@link CustomFeignRequestInterceptor} </p>
 * @author cuisongxu
 * @date 2023/4/2 11:50
 */
public class TrafficLoadBalancerInterceptor extends LoadBalancerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(TrafficLoadBalancerInterceptor.class);

    private final GenericApplicationContext applicationContext;

    private final LoadBalancerClient loadBalancerClient;

    private final LoadBalancerRequestFactory loadBalancerRequestFactory;

    public TrafficLoadBalancerInterceptor(GenericApplicationContext applicationContext,
                                          LoadBalancerClient loadBalancer,
                                          LoadBalancerRequestFactory requestFactory) {
        super(loadBalancer, requestFactory);
        this.applicationContext = applicationContext;
        this.loadBalancerClient = loadBalancer;
        this.loadBalancerRequestFactory = requestFactory;
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        if(!(request instanceof TrafficRequest)) {
            logger.debug("This request will not join full link grayscale");
            return super.intercept(request, body, execution);
        }
        final URI originUri = request.getURI();
        String serviceName = originUri.getHost();

        registerOutBoundServiceNameBean(serviceName);

        addConfigurationsToLoadBalancerClientFactory();

        Assert.notNull(serviceName, "Request URL don't contain a valid hostname: " + originUri);
        return loadBalancerClient.execute(serviceName, this.loadBalancerRequestFactory.createRequest(request, body, execution));
    }

    /**
     * <p>注册serviceName名称的Bean,类型为 {@link LoadBalancerClientSpecification},主要作用为向LoadBalancer的子容器中注册 {@link TrafficLoadBalancerConfiguration}</p>
     * <p>该类是一个配置类，配置类中注册了一个后置处理器 {@link LoadBalancerInstanceSupplierBeanPostProcessor},负责包装 LoadBalancer子容器中的 {@link ServiceInstanceListSupplier}类型的Bean</p>
     * <p>使得所有 {@link ServiceInstanceListSupplier} 中加入全链路灰度自定义的实例过滤逻辑</p
     */
    private void registerOutBoundServiceNameBean(String serviceName) {
        if(!applicationContext.containsBean(serviceName)) {
            applicationContext.registerBean(serviceName, LoadBalancerClientSpecification.class, () -> new LoadBalancerClientSpecification(serviceName, new Class[]{TrafficLoadBalancerConfiguration.class}));
        }
    }

    /**
     * <p>将容器中所有的 {@link LoadBalancerClientSpecification} 类型的Bean加入到 {@link LoadBalancerClientFactory#setConfigurations(List)}中, 使得在创建LoadBalancer子容器时能够加入
     * {@link LoadBalancerClientSpecification} 中自定义的配置类(这里是加入负责包装{@link ServiceInstanceListSupplier}的后置处理器,添加全链路灰度的实例过滤逻辑)</p>
     *
     */
    private void addConfigurationsToLoadBalancerClientFactory() {
        LoadBalancerClientFactory loadBalancerClientFactory = applicationContext.getBean(LoadBalancerClientFactory.class);

        Map<String, LoadBalancerClientSpecification> loadBalancerClientSpecificationMap = applicationContext.getBeansOfType(LoadBalancerClientSpecification.class);
        List<LoadBalancerClientSpecification> loadBalancerClientSpecificationList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(loadBalancerClientSpecificationMap)) {
            loadBalancerClientSpecificationList = new ArrayList<>(loadBalancerClientSpecificationMap.values());
        }

        loadBalancerClientFactory.setConfigurations(loadBalancerClientSpecificationList);
    }
}
